package cn.dengta.webapp.trade.service;

import java.util.*;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import javax.annotation.Resource;
import javax.validation.constraints.NotNull;

import cn.dengta.common.model.Messager;
import cn.dengta.common.model.TimeInterval;
import cn.dengta.common.web.Page;
import cn.dengta.context.model.RedisKey;
import cn.dengta.context.model.WesKit;
import cn.dengta.context.web.RequestContextImpl;
import cn.dengta.webapp.base.dto.AuditEnvConfigDTO;
import cn.dengta.webapp.base.service.BaseServiceImpl;
import cn.dengta.webapp.base.util.MybatisUtil;
import cn.dengta.webapp.trade.dao.UserLimitDao;
import cn.dengta.webapp.trade.entity.UserLimit;
import cn.dengta.webapp.trade.model.UserLimitBizType;
import cn.dengta.webapp.trade.model.UserLimitType;
import cn.dengta.webapp.user.cache.UserCaches;
import cn.dengta.webapp.user.cache.UserSummary;
import cn.dengta.webapp.user.entity.User;
import cn.dengta.webapp.user.model.Gender;
import cn.dengta.webapp.user.model.UserSketch;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import me.codeplayer.util.*;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import static cn.dengta.webapp.base.entity.BaseEntity.ID;
import static cn.dengta.webapp.base.entity.TimeBasedEntity.ADD_TIME;
import static cn.dengta.webapp.trade.entity.UserLimit.*;

/**
 * 服务实现类
 *
 * @author lgy
 * @since 2022-06-16
 */
@Service
@Slf4j
public class UserLimitService extends BaseServiceImpl<UserLimit, UserLimitDao, Long> {

	private static final EasyDate last_date = new EasyDate(2099, 12, 31).endOf(Calendar.DAY_OF_MONTH);

	@Resource
	UserCaches userCaches;
	@Resource
	StringRedisTemplate stringRedisTemplate;

	public Page<UserLimit> findPage(Page<UserLimit> page, @Nullable Long userId, TimeInterval addTimeInterval, Integer type, WesKit wesKit, String domainWesKit, UserLimitBizType limitType) {
		QueryWrapper<UserLimit> wrapper = new QueryWrapper<>();
		wrapper.eq(userId != null, USER_ID, userId)
				.eq(TYPE, type)
				.between(wesKit != null, USER_ID, X.map(wesKit, WesKit::getMinId), X.map(wesKit, WesKit::getMaxId));
		if (limitType != null) {
			wrapper.apply("biz_flag & {0}", limitType.flag);
		}
		wrapper.apply(StringUtil.notEmpty(domainWesKit), domainWesKit);
		MybatisUtil.wrapperInterval(wrapper, ADD_TIME, addTimeInterval);
		wrapper.orderByDesc(ID);
		return selectPage(page, wrapper);
	}

	public List<UserLimit> find(UserLimitType type, Long... userIds) {
		QueryWrapper<UserLimit> wrapper = new QueryWrapper<>();
		wrapper.in(X.isValid(userIds), USER_ID, (Object[]) userIds);
		wrapper.eq(type != null, TYPE, X.map(type, UserLimitType::getValue));
		return selectList(wrapper);
	}

	public void joinLimitData(List<Long> userIds, @NotNull Integer type) {
		Assert.isTrue(X.isValid(userIds));
		Map<Long, UserSummary> summaryMap = userCaches.find(userIds);
		Assert.isTrue(X.size(summaryMap) == X.size(userIds), "系统检验有用户ID不存在，请重新核实！");
		Date now = RequestContextImpl.get().now();
		Map<Long, UserLimit> userLimitMap = CollectionUtil.toHashMap(find(UserLimitType.of(type), userIds.toArray(Long[]::new)), UserLimit::getUserId);
		for (Long userId : userIds) {
			if (!userLimitMap.containsKey(userId)) {
				save(userId, now, type);
			}
		}
	}

	public Messager<UserLimit> remove(Long id) {
		return this.baseDao.deleteLimitById(id) > 0 ? Messager.OK("操作成功！") : Messager.error("删除失败！");
	}

	public boolean canOperate(Long userId, UserLimitType type) {
		QueryWrapper<UserLimit> wrapper = new QueryWrapper<UserLimit>()
				.eq(USER_ID, userId)
				.eq(TYPE, type.value);
		return baseDao.selectCount(wrapper) == 0L;
	}

	private void save(Long userId, Date now, @NotNull Integer type) {
		UserLimit userLimit = new UserLimit().init(now, type, 0 /* 黑名单 */);
		userLimit.setUserId(userId);
		userLimit.setEndDate(last_date.toDate());
		save(userLimit);
	}

	public boolean validateGender(Long fromUserId, Long toUserId) {
		final Map<Long, UserSummary> summaryMap = userCaches.find(fromUserId, toUserId);
		UserSummary fsummary = summaryMap.get(fromUserId);
		UserSummary tsummary = summaryMap.get(toUserId);
		return validateGender(fsummary, tsummary);
	}

	public boolean validateGender(UserSummary fsummary, UserSummary tsummary) {
		WesKit fwk = fsummary.getWesKit();
		WesKit twk = tsummary.getWesKit();
		if (fwk.isInterior() || twk.isInterior()) {
			return fsummary.getGender() != tsummary.getGender();
		}
		return true;
	}

	public void batchUserLimit(Set<Long> userIds, List<Pair<UserLimitBizType, Boolean>> per, UserLimitType type, String remark) {
		if (!X.isValid(userIds)) {
			return;
		}
		final Date now = RequestContextImpl.get().now();
		QueryWrapper<UserLimit> qw = new QueryWrapper<>();
		qw.select(USER_ID, BIZ_FLAG);
		qw.in(USER_ID, userIds);
		qw.eq(TYPE, type.value);
		final List<UserLimit> limits = selectList(qw);
		Assert.isTrue(!X.isValid(limits), "已存在用户ID");
		final List<UserLimit> userLimits = new ArrayList<>(userIds.size());
		for (Long userId : userIds) {
			UserLimit ul = new UserLimit().init(now, type.value, 1);
			ul.setUserId(userId);
			ul.setEndDate(last_date.toDate());
			ul.setRemark(remark);
			ul.addBizFlag(per);
			userLimits.add(ul);
		}
		this.baseDao.batchUserLimit(userLimits);
	}

	/**
	 * 是否包含指定业务标识
	 *
	 * @param userId 用户id
	 * @param bizType 业务标识
	 * @return true 包含
	 */
	public boolean validUserLimitByUserAndBizFlag(Long userId, UserLimitBizType bizType) {
		QueryWrapper<UserLimit> qw = new QueryWrapper<>();
		qw.select(USER_ID, BIZ_FLAG);
		qw.eq(USER_ID, userId);
		qw.eq(TYPE, UserLimitType.USER.value);
		qw.last("LIMIT 1");
		final UserLimit userLimit = selectOne(qw);
		return userLimit != null && userLimit.hasFlag(bizType.getFlag());
	}

	public boolean validUserLimitByUser(Long userId, UserLimitType limitType) {
		QueryWrapper<UserLimit> qw = new QueryWrapper<>();
		qw.select(USER_ID);
		qw.eq(USER_ID, userId);
		qw.eq(TYPE, limitType.value);
		qw.last("LIMIT 1");
		final UserLimit userLimit = selectOne(qw);
		return userLimit != null;
	}

	/**
	 * @return 是否审核环境 true=审核环境；false=正式环境
	 */
	public Predicate<AuditEnvConfigDTO> validAuditEnvFun(UserSketch userSummary) {
		if (!(userSummary instanceof User || userSummary instanceof UserSummary)) {
			return null;
		}
		return dto -> {
			boolean hasAgent = false;
			boolean channel = false;
			if (userSummary instanceof User user) {
				hasAgent = user.hasAgent();
				channel = user.hasChannel();
			}
			if (userSummary instanceof UserSummary user) {
				hasAgent = user.hasAgent();
				channel = user.hasChannel();
			}
			final Gender gender = userSummary.genderOrDefault();
			final Long userId = userSummary.getId();
			boolean maleOpen = Boolean.TRUE.equals(dto.getMale());
			boolean femaleOpen = Boolean.TRUE.equals(dto.getFemale());
			if ((maleOpen || femaleOpen) && validUserLimitByUser(userId, UserLimitType.WHITE_LIST)) {
				return false;
			}
			if (maleOpen && gender.male()) {
				return !channel;
			}
			if (femaleOpen && gender.female()) {
				// 非推广员女用户或者黑名单推广员 审核环境
				return !hasAgent || Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(RedisKey.AGENT_BLACK_AUDIT_SET_DATA, userSummary.getAgentId().toString()));
			}
			return false;
		};
	}

}
